// 
// Copyright 2010 Classpath Pty Ltd <opensource@classpath.com.au>
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 

using sql
using util

**
** `MigrationMain` provides an implementation of `util::AbstractMain` which parses the runtime parameters to perform a migration.
** Implementations of `MigrationMain` must provide:
** - a list of [Migrations]`Migration` to execute (`MigrationMain.migrations`)
** - an instance of `sql::SqlService` to perform migrations with (`MigrationMain.sqlService`)
** - an instance of `Adapter` compatable with the supplied `sql::SqlService` for generating SQL (`MigrationMain.adapter`)
**
abstract class MigrationMain : AbstractMain {

    **
    ** A runtime flag which specifies what `sys::Version` of the database to migrate from.
    **
    @Opt {
        help = "the version of the database to migrate from"
        aliases = ["f"]
    }
    Version? from

    **
    ** A runtime flag which specifies the database should be prepared for migrations prior to running migrations.
    **
    @Opt {
        help = "run interactively (prompts before running each change)"
        aliases = ["i"]
    }
    Bool interactive := false

    **
    ** A runtime flag which specifies the database should be prepared for migrations prior to running migrations.
    **
    @Opt {
        help = "prepares the database to support migrations"
        aliases = ["p"]
    }
    Bool prepare := false

    **
    ** A runtime flag which specifies what `sys::Version` of the database to migrate to.
    **
    @Opt {
        help = "the version of the database to migrate to"
        aliases = ["t"]
    }
    Version? to

    **
    ** A runtime flag which specifies the JDBC uri to connect to.
    **
    @Opt {
        help = "the JDBC uri to connect to"
        aliases = ["u"]
    }
    Str? uri

    **
    ** A runtime flag which specifies the JDBC username to connect with.
    **
    @Opt {
        help = "the JDBC username to connect with"
        aliases = ["n"]
    }
    Str? username

    **
    ** A runtime flag which specifies the JDBC password to connect with.
    **
    @Opt {
        help = "the JDBC password to connect with"
        aliases = ["w"]
    }
    Str? password
  
    **
    ** The user managed list of [Migrations]`Migration` which this instance of `MigrationMain` is able to migrate between.
    **
    abstract Migration[] migrations

    **
    ** The `Adapter` to use in generating SQL based on the [Migrations]`Migration` associated with this instance of `MigrationMain`.
    ** Implementors need to ensure the appropriate `Adapter` is used for the supplied `sql::SqlService`.
    **
    abstract Adapter adapter

    **
    ** Attempts to run the user specified preparation/migration.
    **
    override Int run() {
        Pod.of(this).log.debug("${typeof}.run - start")
        ret := -1
        if (checkUsage) {
            manager := Manager(migrations, adapter, uri, username, password, interactive)
            ok := true
            if (prepare) {
                ok = manager.prepare
            } 
            if (ok && from != to) {
                ok = manager.migrate(from, to)
            }
            ret = ok ? 0 : -2
        }
        Pod.of(this).log.debug("${typeof}.run - end - returning ${ret}")
        return ret
    }

    **
    ** Checks the command line parameters specified by the user for validity.
    ** Valid combinations are:
    ** - only -p (-prepare) is specified - prepares the database and exits
    ** - both -f (-from) and -t (-to) are specified - attempts to migrate the database between the specified versions
    ** - all of -p (-prepare), -f (-from) and -t (to) are specified - prepares the database and then performs the specified migration.
    **
    Bool checkUsage() {
        Pod.of(this).log.debug("${typeof}.checkUsage - start")
        valid := true
        if (uri == null) {
            // TODO test this
            Pod.of(this).log.err("The JDBC uri (-u) is required for all interactions")
            valid = false
        }
        if (prepare) {
            if (from != null && to == null) {
                Pod.of(this).log.err("Can't specify from ('-f') without to ('-t'), even with prepare ('-p')")
                valid = false
            }
            else if (from == null && to != null) {
                Pod.of(this).log.err("Can't specify to ('-t') without from ('-f'), even with prepaer ('-p')")
                valid = false
            }
        }
        else {
            if (from == null && to == null) {
                Pod.of(this).log.err("Must specify both a from ('-f') and to ('-t') version, or prepare ('-p')")
                valid = false
            }
            else if (from != null && to == null) {
                Pod.of(this).log.err("Can't specify from ('-f') without to ('-t')")
                valid = false
            }
            else if (from == null && to != null) {
                Pod.of(this).log.err("Can't specify to ('-t') without from ('-f')")
                valid = false
            }
        }
        Pod.of(this).log.debug("${typeof}.checkUsage - end - returning ${valid}")
        return valid
    }

}
